home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 7
/
Aminet 7 - August 1995.iso
/
Aminet
/
comm
/
tcp
/
AmigaTCP.lha
/
AmigaTCP
/
src
/
ftpcli.c
< prev
next >
Wrap
C/C++ Source or Header
|
1989-06-24
|
15KB
|
686 lines
/* FTP client (interactive user) code */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "icmp.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"
#include "session.h"
#include "cmdparse.h"
#include "dos.h"
extern struct session *current;
extern char nospace[];
#define MAXFILENAME 32
char multiarg[NARG][MAXFILENAME];
int multiargc = 0, curmulti = 0;
struct FileInfoBlock info;
int dodfind = 1;
int docd(),dodir(),doget(),dols(),doput(),dotype(),doabort(), domput();
struct cmds ftpabort[] = {
"abort", doabort, 0, NULLCHAR, NULLCHAR,
NULLCHAR, NULLFP, 0, "Only valid command is \"abort\"", NULLCHAR,
};
struct cmds ftpcmds[] = {
"cd", docd, 2, "cd <directory>", "Could not change directory",
"dir", dodir, 0, NULLCHAR, "Could not complete dir",
"list", dodir, 0, NULLCHAR, "Could not complete dir",
"get", doget, 0, NULLCHAR, "Could not complete get",
"ls", dols, 0, NULLCHAR, "Could not complete ls",
"mput", domput, 0, NULLCHAR, "Could not complete mput",
"nlst", dols, 0, NULLCHAR, "Could not complete ls",
"put", doput, 0, NULLCHAR, "Could not complete put",
"type", dotype, 0, NULLCHAR, "Could not complete type",
NULLCHAR, NULLFP, 0, NULLCHAR, NULLCHAR,
};
/* Handle top-level FTP command */
doftp(argc,argv)
int argc;
char *argv[];
{
int32 aton();
int ftpparse();
char *inet_ntoa();
void r_ctl(),s_ctl();
struct session *s,*newsession();
struct ftp *ftp,*ftp_create();
struct tcb *tcb;
struct socket lsocket,fsocket;
lsocket.address = ip_addr;
lsocket.port = lport++;
fsocket.address = aton(argv[1]);
if(fsocket.address == ip_addr){
printf("FTPing to yourself not supported\r\n");
return 1;
}
if(argc < 3)
fsocket.port = FTP_PORT;
else
fsocket.port = atoi(argv[2]);
/* Allocate a session control block */
if((s = newsession()) == NULLSESSION){
printf("Too many sessions\r\n");
return 1;
}
current = s;
s->type = FTP;
s->parse = ftpparse;
/* Allocate an FTP control block */
if((ftp = ftp_create(0)) == NULLFTP){
s->type = FREE;
printf(nospace);
return 1;
}
ftp->state = COMMAND_STATE;
s->cb.ftp = ftp; /* Downward link */
ftp->session = s; /* Upward link */
/* Now open the control connection */
tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
0,r_ctl,NULLVFP,s_ctl,0,(int *)ftp);
if(tcb == NULLTCB || tcb->state == CLOSED){
/* This is actually a bit dirty here. About the only time the
* state will ever be closed here is if we tried to connect to
* ourselves and got RST'ed. If this is true then the close
* upcall will already have freed the TCB and the FTP block,
* so we're looking at the TCB after it's been freed.
*/
s->type = FREE;
return 0;
}
ftp->control = tcb;
go();
return 0;
}
/* Parse user FTP commands */
int
ftpparse(line,len)
char *line;
int16 len;
{
struct mbuf *bp;
if(current->cb.ftp->state != COMMAND_STATE){
/* The only command allowed in data transfer state is ABORT */
if(cmdparse(ftpabort,line) == -1){
printf("Transfer in progress; only ABORT is acceptable\r\n");
}
fflush(stdout);
return;
}
/* Save it now because cmdparse modifies the original */
bp = qdata(line,len);
if(cmdparse(ftpcmds,line) == -1){
/* Send it direct */
if(bp != NULLBUF)
send_tcp(current->cb.ftp->control,bp);
else
printf(nospace);
} else {
free_p(bp);
}
fflush(stdout);
}
/* Translate 'cd' to 'cwd' for convenience */
static
int
docd(argc,argv)
int argc;
char *argv[];
{
register struct ftp *ftp;
ftp = current->cb.ftp;
return sndmsg(ftp,"CWD %s\r\n",argv[1]);
}
/* Handle "type" command from user */
static
int
dotype(argc,argv)
int argc;
char *argv[];
{
register struct ftp *ftp;
ftp = current->cb.ftp;
if(argc < 2){
switch(ftp->type){
case IMAGE_TYPE:
printf("Image\r\n");
break;
case ASCII_TYPE:
printf("Ascii\r\n");
break;
}
return 0;
}
switch(*argv[1]){
case 'i':
case 'b':
ftp->type = IMAGE_TYPE;
break;
case 'a':
ftp->type = ASCII_TYPE;
break;
default:
printf("Invalid type %s\r\n",argv[1]);
return 1;
}
/* Send a TYPE message */
return sndmsg(ftp,"TYPE %s\r\n",(ftp->type == ASCII_TYPE) ? "A" : "I");
}
/* Start receive transfer. Syntax: get <remote name> [<local name>] */
static
doget(argc,argv)
int argc;
char *argv[];
{
void r_ftpd(),s_ftp();
char *index(),*remotename,*localname;
register struct ftp *ftp;
ftp = current->cb.ftp;
if(ftp == NULLFTP){
printf("Not an FTP session!\r\n");
return 1;
}
if(argc < 2){
printf("File?\r\n");
return 1;
}
remotename = argv[1];
if(argc < 3)
localname = remotename;
else
localname = argv[2];
if(ftp->fp != NULLFILE)
fclose(ftp->fp);
if((ftp->fp = fopen(localname,"w")) == NULLFILE){
printf("Cannot write %s\r\n",localname);
return 1;
}
ftp->state = RECEIVING_STATE;
ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
/* Generate the command to start the transfer */
return sndmsg(ftp,"RETR %s\r\n",remotename);
}
/* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
static
dodir(argc,argv)
int argc;
char *argv[];
{
void r_ftpd(),s_ftp();
char *index(),*localname;
register struct ftp *ftp;
ftp = current->cb.ftp;
if(ftp == NULLFTP){
printf("Not an FTP session!\r\n");
return 1;
}
if(argc < 3)
#ifdef CPM
localname = "con:";
#endif
#ifdef MSDOS
localname = "con";
#endif
#ifdef UNIX
localname = "/dev/tty";
#endif
#ifdef XENIX
localname = "/dev/tty";
#endif
#ifdef AMIGA
localname = "*";
#endif AMIGA
else
localname = argv[2];
if(ftp->fp != NULLFILE)
fclose(ftp->fp);
if((ftp->fp = fopen(localname,"w")) == NULLFILE){
printf("Cannot write %s\r\n",localname);
return 1;
}
ftp->state = RECEIVING_STATE;
ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
/* Generate the command to start the transfer
* It's done this way to avoid confusing the 4.2 FTP server
* if there's no argument
*/
if(argc > 1)
return sndmsg(ftp,"LIST %s\r\n",argv[1]);
else
return sndmsg(ftp,"LIST\r\n","");
}
/* Abbreviated (name only) list of remote directory.
* Syntax: ls <remote directory/file> [<local name>]
*/
static
dols(argc,argv)
int argc;
char *argv[];
{
void r_ftpd(),s_ftp();
char *index(),*localname;
register struct ftp *ftp;
ftp = current->cb.ftp;
if(ftp == NULLFTP){
printf("Not an FTP session!\r\n");
return 1;
}
if(argc < 3)
#ifdef CPM
localname = "con:";
#endif
#ifdef MSDOS
localname = "con";
#endif
#ifdef UNIX
localname = "/dev/tty";
#endif
#ifdef XENIX
localname = "/dev/tty";
#endif
#ifdef AMIGA
localname = "*";
#endif AMIGA
else
localname = argv[2];
if(ftp->fp != NULLFILE)
fclose(ftp->fp);
if((ftp->fp = fopen(localname,"w")) == NULLFILE){
printf("Cannot write %s\r\n",localname);
return 1;
}
ftp->state = RECEIVING_STATE;
ftpsetup(ftp,r_ftpd,NULLVFP,s_ftp);
/* Generate the command to start the transfer */
if(argc > 1)
return sndmsg(ftp,"NLST %s\r\n",argv[1]);
else
return sndmsg(ftp,"NLST\r\n","");
}
doone(localname, remotename)
char *localname, *remotename;
{
void t_ftpd(),s_ftp();
struct ftp *ftp;
if((ftp = current->cb.ftp) == NULLFTP){
printf("Not an FTP session!\r\n");
return 1;
}
if(ftp->fp != NULLFILE)
fclose(ftp->fp);
if((ftp->fp = fopen(localname,"r")) == NULLFILE){
printf("Cannot read %s\r\n",localname);
return 1;
}
ftp->state = SENDING_STATE;
ftpsetup(ftp,NULLVFP,t_ftpd,s_ftp);
/* Generate the command to start the transfer */
return sndmsg(ftp,"STOR %s\r\n",remotename);
}
/* Start transmit. Syntax: put <local name> [<remote name>] */
static
doput(argc,argv)
int argc;
char *argv[];
{
char *remotename,*localname;
if(argc < 2){
printf("File?\r\n");
return 1;
}
localname = argv[1];
if(argc < 3)
remotename = localname;
else
remotename = argv[2];
doone(localname, remotename);
}
donextmulti()
{
int error;
struct ftp *ftp;
if((ftp = current->cb.ftp) == NULLFTP){
printf("Not an FTP session!\r\n");
return 1;
}
again:
if (multiargc > 0)
{
if (dodfind)
{
error = dfind(&info, multiarg[curmulti], 0);
if (error < 0)
printf("No files matching %s\n", multiarg[curmulti]);
dodfind = 0;
}
else
error = dnext(&info);
if (error == 0)
doone(info.fib_FileName, info.fib_FileName);
else
{
curmulti++;
multiargc--;
dodfind = 1;
goto again;
}
}
if (multiargc == 0)
{
printf("Multi put is done\n");
ftp->state = COMMAND_STATE;
}
}
/* Start transmit. Syntax: put <local name> [<remote name>] */
static
domput(argc,argv)
int argc;
char *argv[];
{
int i;
char *remotename,*localname;
struct ftp *ftp;
if((ftp = current->cb.ftp) == NULLFTP){
printf("Not an FTP session!\r\n");
return 1;
}
if(argc < 2){
printf("File list?\r\n");
return 1;
}
for(i = 0; i < argc - 1; i++)
strncpy(multiarg[i], argv[i+1], MAXFILENAME);
multiargc = argc - 1;
for(i = 0; i < multiargc; i++)
printf("copy %s out\n", multiarg[i]);
curmulti = 0;
dodfind = 1;
donextmulti();
}
/* Abort a GET or PUT operation in progress. Note: this will leave
* the partial file on the local or remote system
*/
doabort(argc,argv)
int argc;
char *argv[];
{
register struct ftp *ftp;
ftp = current->cb.ftp;
/* Close the local file */
fclose(ftp->fp);
ftp->fp = NULLFILE;
switch(ftp->state){
case SENDING_STATE:
/* Send a premature EOF.
* Unfortunately we can't just reset the connection
* since the remote side might end up waiting forever
* for us to send something.
*/
close_tcp(ftp->data);
multiargc = 0;
printf("Put aborted\r\n");
break;
case RECEIVING_STATE:
/* Just exterminate the data channel TCB; this will
* generate a RST on the next data packet which will
* abort the sender
*/
del_tcp(ftp->data);
ftp->data = NULLTCB;
printf("Get aborted\r\n");
break;
}
ftp->state = COMMAND_STATE;
fflush(stdout);
}
/* create data port, and send PORT message */
static
ftpsetup(ftp,recv,send,state)
struct ftp *ftp;
void (*send)();
void (*recv)();
void (*state)();
{
struct socket lsocket,fsocket;
struct mbuf *bp;
lsocket.address = ip_addr;
lsocket.port = lport++;
fsocket.address = ftp->control->conn.remote.address;
fsocket.port = FTPD_PORT;
/* Compose and send PORT a,a,a,a,p,p message */
if((bp = alloc_mbuf(35)) == NULLBUF){ /* 5 more than worst case */
printf(nospace);
return;
}
/* I know, this looks gross, but it works! */
sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
hibyte(hiword(lsocket.address)),
lobyte(hiword(lsocket.address)),
hibyte(loword(lsocket.address)),
lobyte(loword(lsocket.address)),
hibyte(lsocket.port),
lobyte(lsocket.port));
bp->cnt = strlen(bp->data);
send_tcp(ftp->control,bp);
/* Post a listen on the data connection */
ftp->data = open_tcp(&lsocket,&fsocket,TCP_PASSIVE,0,
recv,send,state,0,(int *)ftp);
}
/* FTP control channel receiver upcall routine */
void
r_ctl(tcb,cnt)
register struct tcb *tcb;
int16 cnt;
{
struct mbuf *bp;
struct ftp *ftp;
if((ftp = (struct ftp *)tcb->user) == NULLFTP){
/* Unknown connection; kill it */
close_tcp(tcb);
return;
}
/* Hold output if we're not the current session */
if(mode != CONV_MODE || current == NULLSESSION || current->cb.ftp != ftp)
return;
if(recv_tcp(tcb,&bp,cnt) > 0){
while(bp != NULLBUF){
#ifndef AMIGA
fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
#else
register unsigned len = bp->cnt;
register char *dp = bp->data;
while (len--)
putchar(*dp++);
#endif
bp = free_mbuf(bp);
}
fflush(stdout);
}
}
/* Control channel state change upcall routine */
static
void
s_ctl(tcb,old,new)
register struct tcb *tcb;
char old,new;
{
void ftp_delete();
struct ftp *ftp;
char notify = 0;
extern char *tcpstates[];
extern char *reasons[];
extern char *unreach[];
extern char *exceed[];
/* Can't add a check for unknown connection here, it would loop
* on a close upcall! We're just careful later on.
*/
ftp = (struct ftp *)tcb->user;
if(current != NULLSESSION && current->cb.ftp == ftp)
notify = 1;
switch(new){
case CLOSE_WAIT:
if(notify)
printf("%s\r\n",tcpstates[new]);
close_tcp(tcb);
break;
case CLOSED: /* heh heh */
if(notify){
printf("%s (%s",tcpstates[new],reasons[tcb->reason]);
if(tcb->reason == NETWORK){
switch(tcb->type){
case DEST_UNREACH:
printf(": %s unreachable",unreach[tcb->code]);
break;
case TIME_EXCEED:
printf(": %s time exceeded",exceed[tcb->code]);
break;
}
}
printf(")\r\n");
cmdmode();
}
del_tcp(tcb);
if(ftp != NULLFTP)
ftp_delete(ftp);
break;
default:
if(notify)
printf("%s\r\n",tcpstates[new]);
break;
}
if(notify)
fflush(stdout);
}
/* FTP client data channel connection state change upcall handler */
static
void
s_ftp(tcb,old,new)
struct tcb *tcb;
char old,new;
{
struct ftp *ftp;
if((ftp = (struct ftp *)tcb->user) == NULLFTP){
/* Unknown connection, kill it */
close_tcp(tcb);
return;
}
switch(new){
case FINWAIT2:
case TIME_WAIT:
if(ftp != NULLFTP && ftp->state == SENDING_STATE){
/* We've received an ack of our FIN, so
* return to command mode
*/
if (multiargc <= 0)
{
printf("Multiput done\r\n");
ftp->state = COMMAND_STATE;
}
if(current != NULLSESSION && current->cb.ftp == ftp){
printf("Put complete, %lu bytes sent\r\n",
tcb->snd.una - tcb->iss - 2);
fflush(stdout);
}
}
break;
case CLOSE_WAIT:
close_tcp(tcb);
if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
/* End of file received on incoming file */
#ifdef CPM
if(ftp->type == ASCII_TYPE)
putc(CTLZ,ftp->fp);
#endif
fclose(ftp->fp);
ftp->fp = NULLFILE;
ftp->state = COMMAND_STATE;
if(current != NULLSESSION && current->cb.ftp == ftp){
printf("Get complete, %lu bytes received\r\n",
tcb->rcv.nxt - tcb->irs - 2);
fflush(stdout);
}
}
break;
case CLOSED:
if(ftp != NULLFTP)
ftp->data = NULLTCB;
del_tcp(tcb);
if (multiargc > 0)
donextmulti();
break;
}
}
/* Send a message on the control channel */
/*VARARGS*/
static
int
sndmsg(ftp,fmt,arg)
struct ftp *ftp;
char *fmt;
char *arg;
{
struct mbuf *bp;
int16 len;
len = strlen(fmt) + strlen(arg) + 10; /* fudge factor */
if((bp = alloc_mbuf(len)) == NULLBUF){
printf(nospace);
return 1;
}
sprintf(bp->data,fmt,arg);
bp->cnt = strlen(bp->data);
send_tcp(ftp->control,bp);
return 0;
}